import numpy as np
import torch
from torch.utils import data
from d2l import torch as d2l
from torch import nn


def synthetic_data(w, b, num_examples):
    X = torch.normal(0, 1, (num_examples, len(w)))
    y = torch.matmul(X, w) + b
    y += torch.normal(0, 0.01, y.shape)
    return X, y.reshape((-1, 1))


def load_array(data_arrays, batch_size, is_train=True):  # @save
    """构造一个PyTorch数据迭代器"""
    dataset = data.TensorDataset(*data_arrays)
    return data.DataLoader(dataset, batch_size, shuffle=is_train)


if __name__ == '__main__':
    true_w = torch.tensor([2, -3.4])
    true_b = 4.2
    features, labels = synthetic_data(true_w, true_b, 1000)

    batch_size = 10
    # 经过下面操作，数据将被切分为100个小的数据集
    data_iter = load_array((features, labels), batch_size)

    # w = torch.normal(0, 0.01, size=(2, 1), requires_grad=True)
    # b = torch.zeros(1, requires_grad=True)

    net = nn.Sequential(nn.Linear(2, 1))
    # 下面两种方法等价，经测试，初始权重都为0或其他数字，也可以计算出正确结果
    # net[0].weight.data.fill_(0)
    # net[0].weight.data.zero_(0)
    net[0].weight.data.normal_(0, 0.01)
    net[0].bias.data.fill_(0)
    # 均方差损失
    loss = nn.MSELoss()
    #交叉熵损失
    # loss = nn.CrossEntropyLoss()
    trainer = torch.optim.SGD(net.parameters(), lr=0.03)

    num_epochs = 3
    for epoch in range(num_epochs):
        for X, y in data_iter:
            l = loss(net(X), y)
            trainer.zero_grad()
            l.backward()
            trainer.step()
        l = loss(net(features), labels)
        print(f'epoch {epoch + 1}, loss {l:f}')
    w = net[0].weight.data
    print('w：', w)

    print('w的估计误差：', true_w - w.reshape(true_w.shape))
    b = net[0].bias.data
    print('b：', b)
    print('b的估计误差：', true_b - b)
